<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="robots" content="noodp" />
        <title>【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记 - L_B__</title><meta name="referrer" content="no-referrer">
<meta name="description" content="jwt"><meta property="og:title" content="【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记" />
<meta property="og:description" content="jwt" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://acking-you.github.io/posts/jwt/" /><meta property="og:image" content="https://acking-you.github.io/logo.png"/><meta property="article:section" content="posts" />
<meta property="article:published_time" content="2022-05-17T00:00:00+00:00" />
<meta property="article:modified_time" content="2022-05-17T00:00:00+00:00" />

<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://acking-you.github.io/logo.png"/>

<meta name="twitter:title" content="【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记"/>
<meta name="twitter:description" content="jwt"/>
<meta name="application-name" content="FeelIt">
<meta name="apple-mobile-web-app-title" content="FeelIt"><meta name="theme-color" content="#ffffff"><meta name="msapplication-TileColor" content="#da532c"><link rel="canonical" href="https://acking-you.github.io/posts/jwt/" /><link rel="prev" href="https://acking-you.github.io/posts/%E4%B8%80%E6%96%87%E8%BF%9B%E5%85%A5javaweb/" /><link rel="next" href="https://acking-you.github.io/posts/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" /><link rel="stylesheet" href="/css/page.min.css"><link rel="stylesheet" href="/css/home.min.css"><script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "BlogPosting",
        "headline": "【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记",
        "inLanguage": "zh-CN",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": "https:\/\/acking-you.github.io\/posts\/jwt\/"
        },"genre": "posts","keywords": "用户鉴权是什么？jwt是什么？","wordcount":  2988 ,
        "url": "https:\/\/acking-you.github.io\/posts\/jwt\/","datePublished": "2022-05-17T00:00:00+00:00","dateModified": "2022-05-17T00:00:00+00:00","publisher": {
            "@type": "Organization",
            "name": "作者"},"author": {
                "@type": "Person",
                "name": "作者"
            },"description": "jwt"
    }
    </script></head><body data-header-desktop="auto" data-header-mobile="auto"><script>(window.localStorage && localStorage.getItem('theme') ? localStorage.getItem('theme') === 'dark' : ('auto' === 'auto' ? window.matchMedia('(prefers-color-scheme: dark)').matches : 'auto' === 'dark')) && document.body.setAttribute('theme', 'dark');</script>

        <div id="mask"></div><div class="wrapper"><header class="desktop" id="header-desktop">
    <div class="header-wrapper">
        <div class="header-title">
            <a href="/" title="L_B__">L_B__</a>
        </div>
        <div class="menu">
            <div class="menu-inner"><a class="menu-item" href="/posts/"> 文章 </a><a class="menu-item" href="/tags/"> 标签 </a><a class="menu-item" href="/categories/"> 分类 </a><span class="menu-item delimiter"></span><span class="menu-item search" id="search-desktop">
                        <input type="text" placeholder="搜索文章标题或内容..." id="search-input-desktop">
                        <a href="#" class="search-button search-toggle" id="search-toggle-desktop" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="#" class="search-button search-clear" id="search-clear-desktop" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-desktop">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </span><a href="javascript:void(0);" class="menu-item theme-switch" title="切换主题">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div>
        </div>
    </div>
</header><header class="mobile" id="header-mobile">
    <div class="header-container">
        <div class="header-wrapper">
            <div class="header-title">
                <a href="/" title="L_B__">L_B__</a>
            </div>
            <div class="menu-toggle" id="menu-toggle-mobile">
                <span></span><span></span><span></span>
            </div>
        </div>
        <div class="menu" id="menu-mobile"><div class="search-wrapper">
                    <div class="search mobile" id="search-mobile">
                        <input type="text" placeholder="搜索文章标题或内容..." id="search-input-mobile">
                        <a href="#" class="search-button search-toggle" id="search-toggle-mobile" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="#" class="search-button search-clear" id="search-clear-mobile" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-mobile">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </div>
                    <a href="#" class="search-cancel" id="search-cancel-mobile">
                        取消
                    </a>
                </div><a class="menu-item" href="/posts/" title="">文章</a><a class="menu-item" href="/tags/" title="">标签</a><a class="menu-item" href="/categories/" title="">分类</a><div class="menu-item"><a href="javascript:void(0);" class="theme-switch" title="切换主题">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div></div>
    </div>
</header><div class="search-dropdown desktop">
    <div id="search-dropdown-desktop"></div>
</div>
<div class="search-dropdown mobile">
    <div id="search-dropdown-mobile"></div>
</div><main class="main">
                <div class="container"><div class="toc" id="toc-auto">
            <h2 class="toc-title">目录</h2>
            <div class="toc-content" id="toc-content-auto"></div>
        </div><article class="page single" data-toc="disable"><div class="featured-image"><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/img_convert/6a656bf83ac631cc9afaeaef07661809.png#pic_center"
        data-srcset="https://img-blog.csdnimg.cn/img_convert/6a656bf83ac631cc9afaeaef07661809.png#pic_center, https://img-blog.csdnimg.cn/img_convert/6a656bf83ac631cc9afaeaef07661809.png#pic_center 1.5x, https://img-blog.csdnimg.cn/img_convert/6a656bf83ac631cc9afaeaef07661809.png#pic_center 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/img_convert/6a656bf83ac631cc9afaeaef07661809.png#pic_center"
        title="jwt" /></div><div class="single-card" data-image="true"><h2 class="single-title animated flipInX">【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记</h2><div class="post-meta">
                <div class="post-meta-line"><span class="post-author"><a href="/" title="Author" rel=" author" class="author"><i class="fas fa-user-circle fa-fw"></i>作者</a></span>&nbsp;<span class="post-category">出版于  <a href="/categories/%E9%9D%92%E8%AE%AD%E8%90%A5%E7%AC%94%E8%AE%B0/"><i class="far fa-folder fa-fw"></i>青训营笔记</a></span></div>
                <div class="post-meta-line"><span><i class="far fa-calendar-alt fa-fw"></i>&nbsp;<time datetime="2022-05-17">2022-05-17</time></span>&nbsp;<span><i class="fas fa-pencil-alt fa-fw"></i>&nbsp;约 2988 字</span>&nbsp;
                    <span><i class="far fa-clock fa-fw"></i>&nbsp;预计阅读 6 分钟</span>&nbsp;</div>
            </div>
            
            <hr><div class="details toc" id="toc-static"  data-kept="">
                    <div class="details-summary toc-title">
                        <span>目录</span>
                        <span><i class="details-icon fas fa-angle-right"></i></span>
                    </div>
                    <div class="details-content toc-content" id="toc-content-static"><nav id="TableOfContents">
  <ul>
    <li><a href="#用户认证">用户认证</a></li>
    <li><a href="#jwt到底是什么">JWT到底是什么</a>
      <ul>
        <li><a href="#jwt原理">JWT原理</a></li>
        <li><a href="#jwt数据结构">JWT数据结构</a></li>
      </ul>
    </li>
    <li><a href="#jwt实战">JWT实战</a>
      <ul>
        <li><a href="#签发jwt的token">签发JWT的token</a></li>
        <li><a href="#解析jwt的token">解析JWT的token</a></li>
      </ul>
    </li>
    <li><a href="#jwt的几个特点总结">JWT的几个特点总结</a></li>
  </ul>
</nav></div>
                </div><div class="content" id="content"><p>这是我参与「第三届青训营 -后端场」笔记创作活动的的第四篇笔记。</p>
<blockquote>
<p>由于最近14周忙的焦头烂额，所以一直没有时间写笔记，而且也很久没有认真看青训营的课了（期末考试，所有专业课都放在了14周考试，考完这个周，后面就没有重要的考试了（6级英语和蓝桥杯国赛除外😭</p>
</blockquote>
<p>我为什么突然将JWT鉴权，因为抖音项目里面的前后端身份识别就是通过传递JWT token进行身份验证的传递。</p>
<p>JSON Web Token（缩写 JWT）是目前最流行的跨域认证解决方案，本文介绍它的原理和用法。</p>
<h2 id="用户认证">用户认证</h2>
<p>在服务端和客户端之间通信的过程中，服务端需要有效的识别出客户端的身份，才能进行对应的通信，毕竟大部分时候客户端和服务器并不是长连接，一般都是socket都是连接后收发一个包就断开了，所以这种情景下，每次请求肯定是需要重新经过认证过程的。</p>
<p>互联网服务离不开用户认证。一般流程是下面这样。</p>
<ol>
<li>
<p>用户向服务器发送用户名和密码。</p>
</li>
<li>
<p>服务器验证通过后，在当前对话（session）里面保存相关数据，比如用户角色、登录时间等等。</p>
</li>
<li>
<p>服务器向用户返回一个 session_id，写入用户的 Cookie。</p>
</li>
<li>
<p>用户随后的每一次请求，都会通过 Cookie，将 session_id 传回服务器。</p>
</li>
<li>
<p>服务器收到 session_id，找到前期保存的数据，由此得知用户的身份。</p>
</li>
</ol>
<p>这种模式的问题在于，扩展性不好。单机当然没有问题，如果是服务器集群，或者是跨域的服务导向架构，就要求 session 数据共享，每台服务器都能够读取 session。</p>
<p>举例来说，A 网站和 B 网站是同一家公司的关联服务。现在要求，用户只要在其中一个网站登录，再访问另一个网站就会自动登录，请问怎么实现？</p>
<p>一种解决方案是 session 数据持久化，写入数据库或别的持久层。各种服务收到请求后，都向持久层请求数据。这种方案的优点是架构清晰，缺点是工程量比较大。另外，持久层万一挂了，就会单点失败。</p>
<p>另一种方案是服务器索性不保存 session 数据了，所有数据都保存在客户端，每次请求都发回服务器。JWT 就是这种方案的一个代表。</p>
<h2 id="jwt到底是什么">JWT到底是什么</h2>
<h3 id="jwt原理">JWT原理</h3>
<p>为什么叫做JWT，从缩写中又json，就不难猜出，它的数据格式肯定和json格式有关联。</p>
<p>JWT 的原理是，服务器认证以后，生成一个 JSON 对象，发回给用户，就像下面这样。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="p">{</span>
  <span class="nt">&#34;姓名&#34;</span><span class="p">:</span> <span class="s2">&#34;张三&#34;</span><span class="p">,</span>
  <span class="nt">&#34;角色&#34;</span><span class="p">:</span> <span class="s2">&#34;管理员&#34;</span><span class="p">,</span>
  <span class="nt">&#34;到期时间&#34;</span><span class="p">:</span> <span class="s2">&#34;2022年5月27日0点0分&#34;</span>
<span class="p">}</span>
</code></pre></div><p>之后，用户与服务端通信的时候，都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了<strong>防止用户篡改数据</strong>，服务器在生成这个对象的时候，会加上签名（详见后文）。</p>
<p>服务器就不保存任何 session 数据了，也就是说，服务器变成无状态了，从而比较容易实现扩展。</p>
<h3 id="jwt数据结构">JWT数据结构</h3>
<p>虽然如上文所说，发送的JWT是一个json对象是没错，但实际发送的并不是直接的明文，而应该是由 <code>.</code> 分割的三个字符串。如下图：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f0069621cefa4a99a91fbf1e36a28c63~tplv-k3u1fbpfcp-watermark.image?"
        data-srcset="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f0069621cefa4a99a91fbf1e36a28c63~tplv-k3u1fbpfcp-watermark.image?, https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f0069621cefa4a99a91fbf1e36a28c63~tplv-k3u1fbpfcp-watermark.image? 1.5x, https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f0069621cefa4a99a91fbf1e36a28c63~tplv-k3u1fbpfcp-watermark.image? 2x"
        data-sizes="auto"
        alt="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f0069621cefa4a99a91fbf1e36a28c63~tplv-k3u1fbpfcp-watermark.image?"
        title="jwt最终的形式" /></p>
<p>以上的三个部分的字符串，分别被称为以下三个组成部分：</p>
<ul>
<li>Header（头部）</li>
<li>Payload（负载）</li>
<li>Signature（签名）</li>
</ul>
<p>即 <code>Header.Paload.Signature</code></p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fad7b9579766402e89518455e621c94d~tplv-k3u1fbpfcp-watermark.image?"
        data-srcset="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fad7b9579766402e89518455e621c94d~tplv-k3u1fbpfcp-watermark.image?, https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fad7b9579766402e89518455e621c94d~tplv-k3u1fbpfcp-watermark.image? 1.5x, https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fad7b9579766402e89518455e621c94d~tplv-k3u1fbpfcp-watermark.image? 2x"
        data-sizes="auto"
        alt="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fad7b9579766402e89518455e621c94d~tplv-k3u1fbpfcp-watermark.image?"
        title="jwt组成" /></p>
<p>下面依次介绍这三个部分。</p>
<h4 id="header">Header</h4>
<p>Header 部分是一个 JSON 对象，描述 JWT 的元数据，通常是下面的样子。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="p">{</span>
  <span class="nt">&#34;alg&#34;</span><span class="p">:</span> <span class="s2">&#34;HS256&#34;</span><span class="p">,</span>
  <span class="nt">&#34;typ&#34;</span><span class="p">:</span> <span class="s2">&#34;JWT&#34;</span>
<span class="p">}</span>
</code></pre></div><p>上面代码中，<code>alg</code>属性表示签名的算法（algorithm），默认是 HMAC SHA256（写成 HS256）；<code>typ</code>属性表示这个令牌（token）的类型（type），JWT 令牌统一写为<code>JWT</code>。</p>
<h4 id="payload">Payload</h4>
<p>Payload 部分也是一个 JSON 对象，用来存放实际需要传递的数据。JWT 规定了7个官方字段，供选用。</p>
<ul>
<li>iss (issuer)：签发人</li>
<li>exp (expiration time)：过期时间</li>
<li>sub (subject)：主题</li>
<li>aud (audience)：受众</li>
<li>nbf (Not Before)：生效时间</li>
<li>iat (Issued At)：签发时间</li>
<li>jti (JWT ID)：编号</li>
</ul>
<p>除了官方字段，你还可以在这个部分定义私有字段，下面就是一个例子。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="p">{</span>
  <span class="nt">&#34;sub&#34;</span><span class="p">:</span> <span class="s2">&#34;1234567890&#34;</span><span class="p">,</span>
  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;John Doe&#34;</span><span class="p">,</span>
  <span class="nt">&#34;admin&#34;</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">}</span>
</code></pre></div><p>注意：JWT 默认是不加密的（即便后面的签名也只是为了防止数据篡改），任何人都可以读到，所以不要把秘密信息放在这个部分。</p>
<h4 id="signature">Signature</h4>
<p>Signature 部分是对前两部分的签名，<strong>防止数据篡改</strong>。</p>
<p><strong>首先，需要指定一个密钥（secret）。这个密钥只有服务器才知道，不能泄露给用户</strong>。然后，使用 Header 里面指定的签名算法（默认是 HMAC SHA256），按照下面的公式产生签名。</p>
<pre><code>HMACSHA256(
  base64UrlEncode(header) + &quot;.&quot; +
  base64UrlEncode(payload),
  secret)
</code></pre><p>算出签名以后，把 Header、Payload、Signature 三个部分拼成一个字符串，每个部分之间用&quot;点&quot;（<code>.</code>）分隔，就可以返回给用户。</p>
<h2 id="jwt实战">JWT实战</h2>
<p>根据前面的描述，我们对JWT的数据结构有了一定的了解。</p>
<p>现在转换到我们的Go语言中具体的使用它！</p>
<p>首先go get一下我们处理JWT的包。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="k">go</span> <span class="nx">get</span> <span class="nx">github</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="nx">dgrijalva</span><span class="o">/</span><span class="nx">jwt</span><span class="o">-</span><span class="k">go</span>
</code></pre></div><h3 id="签发jwt的token">签发JWT的token</h3>
<p>根据前面的讲解，我们已经了解到JWT的数据结构，它就是一个json文件，包含三个部分。</p>
<p>那么我们现在需要做的就是填写这三部分，最后通过base64UrlEncode编码得到字符串即为最终的token。</p>
<p><strong>Header部分</strong></p>
<p>这个部分包含两个字段，这两个字段都不用我们填，会由你选择的签名算法自动填写好。</p>
<p><strong>Payload部分</strong></p>
<p>我们查看 <code>jwt.StandardClaims</code> 类型的字段，可以发现它包含如下的类型：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/05dbacf42df94899bc08ff61baf37622~tplv-k3u1fbpfcp-watermark.image?"
        data-srcset="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/05dbacf42df94899bc08ff61baf37622~tplv-k3u1fbpfcp-watermark.image?, https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/05dbacf42df94899bc08ff61baf37622~tplv-k3u1fbpfcp-watermark.image? 1.5x, https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/05dbacf42df94899bc08ff61baf37622~tplv-k3u1fbpfcp-watermark.image? 2x"
        data-sizes="auto"
        alt="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/05dbacf42df94899bc08ff61baf37622~tplv-k3u1fbpfcp-watermark.image?"
        title="image.png" /></p>
<p>这正好就是Payload部分默认填写的几个字段，我们自定义一个结构体，将该结构体类型嵌入。如下，我定义了Claims类型：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Claims</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">UserId</span> <span class="kt">int64</span>
	<span class="nx">jwt</span><span class="p">.</span><span class="nx">StandardClaims</span>
<span class="p">}</span>
</code></pre></div><p>然后我们把这些字段填充完整，来用它得到token。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="nx">claims</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">Claims</span><span class="p">{</span>
    <span class="nx">UserId</span><span class="p">:</span> <span class="nx">user</span><span class="p">.</span><span class="nx">UserInfoId</span><span class="p">,</span>
    <span class="nx">StandardClaims</span><span class="p">:</span> <span class="nx">jwt</span><span class="p">.</span><span class="nx">StandardClaims</span><span class="p">{</span>
        <span class="nx">ExpiresAt</span><span class="p">:</span> <span class="nx">expirationTime</span><span class="p">.</span><span class="nf">Unix</span><span class="p">(),</span>
        <span class="nx">IssuedAt</span><span class="p">:</span>  <span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">().</span><span class="nf">Unix</span><span class="p">(),</span>
        <span class="nx">Issuer</span><span class="p">:</span>    <span class="s">&#34;douyin_pro_131&#34;</span><span class="p">,</span>
        <span class="nx">Subject</span><span class="p">:</span>   <span class="s">&#34;L_B__&#34;</span><span class="p">,</span>
    <span class="p">}}</span>
<span class="nx">token</span> <span class="o">:=</span> <span class="nx">jwt</span><span class="p">.</span><span class="nf">NewWithClaims</span><span class="p">(</span><span class="nx">jwt</span><span class="p">.</span><span class="nx">SigningMethodHS256</span><span class="p">,</span> <span class="nx">claims</span><span class="p">)</span>
</code></pre></div><p><code>jwt.NewWithClaims()</code> 将返回一个 <code>Token</code> 对象。</p>
<p>看看源码，我们发现它仅仅构造了Token对象并返回，里面包含了你已经确认的签名方法回调。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cfe030fb35e34666894d22b54efe0731~tplv-k3u1fbpfcp-watermark.image?"
        data-srcset="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cfe030fb35e34666894d22b54efe0731~tplv-k3u1fbpfcp-watermark.image?, https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cfe030fb35e34666894d22b54efe0731~tplv-k3u1fbpfcp-watermark.image? 1.5x, https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cfe030fb35e34666894d22b54efe0731~tplv-k3u1fbpfcp-watermark.image? 2x"
        data-sizes="auto"
        alt="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cfe030fb35e34666894d22b54efe0731~tplv-k3u1fbpfcp-watermark.image?"
        title="image.png" /></p>
<p><strong>Signature部分</strong></p>
<p>最后通过得到的 <code>Token</code> 对象调用它的 <code>SignedString</code> 方法并传入产生签名需要的密钥(自定义byte[]类型的数据)，将会自动完成token的整个编码过程：包括对 <code>Signature</code> 字段的填写，以及将上述三个部分json数据通过base64url进行最后的编码返回最终的token字符串。</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5dee5a93382a4779aa8e7fe2466d25a1~tplv-k3u1fbpfcp-watermark.image?"
        data-srcset="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5dee5a93382a4779aa8e7fe2466d25a1~tplv-k3u1fbpfcp-watermark.image?, https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5dee5a93382a4779aa8e7fe2466d25a1~tplv-k3u1fbpfcp-watermark.image? 1.5x, https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5dee5a93382a4779aa8e7fe2466d25a1~tplv-k3u1fbpfcp-watermark.image? 2x"
        data-sizes="auto"
        alt="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5dee5a93382a4779aa8e7fe2466d25a1~tplv-k3u1fbpfcp-watermark.image?"
        title="image.png" /></p>
<h4 id="完整源码">完整源码</h4>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf8e1c133b4c413b8f1b213d95f13445~tplv-k3u1fbpfcp-watermark.image?"
        data-srcset="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf8e1c133b4c413b8f1b213d95f13445~tplv-k3u1fbpfcp-watermark.image?, https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf8e1c133b4c413b8f1b213d95f13445~tplv-k3u1fbpfcp-watermark.image? 1.5x, https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf8e1c133b4c413b8f1b213d95f13445~tplv-k3u1fbpfcp-watermark.image? 2x"
        data-sizes="auto"
        alt="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cf8e1c133b4c413b8f1b213d95f13445~tplv-k3u1fbpfcp-watermark.image?"
        title="image.png" /></p>
<h3 id="解析jwt的token">解析JWT的token</h3>
<p>解析过程就非常的简单了，这相当于一个反序列化的过程，和json的解析过程类似，jwt提供的函数需要传入对象的指针，然后在内部帮你完整反序列化操作绑定到这个对象，唯一的不一样就是我们还需要传入一个回调，这个回调返回密钥以便最终的签名判断防止数据篡改。</p>
<p>代码如下：</p>
<p><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/530c654a903343d782a7c2abda9476b6~tplv-k3u1fbpfcp-watermark.image?"
        data-srcset="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/530c654a903343d782a7c2abda9476b6~tplv-k3u1fbpfcp-watermark.image?, https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/530c654a903343d782a7c2abda9476b6~tplv-k3u1fbpfcp-watermark.image? 1.5x, https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/530c654a903343d782a7c2abda9476b6~tplv-k3u1fbpfcp-watermark.image? 2x"
        data-sizes="auto"
        alt="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/530c654a903343d782a7c2abda9476b6~tplv-k3u1fbpfcp-watermark.image?"
        title="image.png" /></p>
<p>注意：JWT解析过程中，它是会判断token是否过期了，如果过期，那么就会返回err。</p>
<p>最后我们服务端得到claims后，便可得到其中包含的用户信息了，然后对该用户执行对应的操作，比如得到该用户喜欢的视频列表。</p>
<p>以上所有代码均出自我写的抖音项目里的 <code>jwt.go</code> 目录中。</p>
<p><a href="https://github.com/ACking-you/byte_douyin_project/blob/master/service/jwt.go" target="_blank" rel="noopener noreffer">源码链接</a></p>
<h2 id="jwt的几个特点总结">JWT的几个特点总结</h2>
<p>（1）JWT 默认是不加密，但也是可以加密的。生成原始 Token 以后，可以用密钥再加密一次。</p>
<p>（2）JWT 不加密的情况下，不能将秘密数据写入 JWT。</p>
<p>（3）JWT 不仅可以用于认证，也可以用于交换信息。有效使用 JWT，可以降低服务器查询数据库的次数。</p>
<p>（4）JWT 的最大缺点是，由于服务器不保存 session 状态，因此无法在使用过程中废止某个 token，或者更改 token 的权限。也就是说，一旦 JWT 签发了，在到期之前就会始终有效，除非服务器部署额外的逻辑。</p>
<p>（5）JWT 本身包含了认证信息，一旦泄露，任何人都可以获得该令牌的所有权限。为了减少盗用，JWT 的有效期应该设置得比较短。对于一些比较重要的权限，使用时应该再次对用户进行认证。</p>
<p>（6）为了减少盗用，JWT 不应该使用 HTTP 协议明码传输，要使用 HTTPS 协议传输。</p>
<blockquote>
<p>参考链接：</p>
<p><a href="https://jwt.io/introduction/" target="_blank" rel="noopener noreffer">https://jwt.io/introduction/</a></p>
<p><a href="https://github.com/dwyl/learn-json-web-tokens/blob/master/README.md" target="_blank" rel="noopener noreffer">learn how to use jwt?</a></p>
</blockquote>
</div><div class="post-footer" id="post-footer">
    <div class="post-info"><div class="post-info-tag"><span><a href="/tags/%E7%94%A8%E6%88%B7%E9%89%B4%E6%9D%83%E6%98%AF%E4%BB%80%E4%B9%88jwt%E6%98%AF%E4%BB%80%E4%B9%88/">用户鉴权是什么？jwt是什么？</a>
                </span></div><div class="post-info-line"><div class="post-info-mod">
                <span>更新于 2022-05-17</span>
            </div><div class="post-info-mod"></div>
        </div><div class="post-info-share">
            <span><a href="javascript:void(0);" title="分享到 Twitter" data-sharer="twitter" data-url="https://acking-you.github.io/posts/jwt/" data-title="【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记" data-hashtags="用户鉴权是什么？jwt是什么？"><i class="fab fa-twitter fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Facebook" data-sharer="facebook" data-url="https://acking-you.github.io/posts/jwt/" data-hashtag="用户鉴权是什么？jwt是什么？"><i class="fab fa-facebook-square fa-fw"></i></a><a href="javascript:void(0);" title="分享到 WhatsApp" data-sharer="whatsapp" data-url="https://acking-you.github.io/posts/jwt/" data-title="【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记" data-web><i class="fab fa-whatsapp fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Line" data-sharer="line" data-url="https://acking-you.github.io/posts/jwt/" data-title="【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记"><i class="fab fa-line fa-fw"></i></a><a href="javascript:void(0);" title="分享到 微博" data-sharer="weibo" data-url="https://acking-you.github.io/posts/jwt/" data-title="【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记" data-image="https://img-blog.csdnimg.cn/img_convert/6a656bf83ac631cc9afaeaef07661809.png#pic_center"><i class="fab fa-weibo fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Myspace" data-sharer="myspace" data-url="https://acking-you.github.io/posts/jwt/" data-title="【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记" data-description="jwt"><i data-svg-src="/lib/simple-icons/icons/myspace.min.svg"></i></a><a href="javascript:void(0);" title="分享到 Blogger" data-sharer="blogger" data-url="https://acking-you.github.io/posts/jwt/" data-title="【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记" data-description="jwt"><i class="fab fa-blogger fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Evernote" data-sharer="evernote" data-url="https://acking-you.github.io/posts/jwt/" data-title="【项目必会】用户鉴权是什么？jwt是什么？ | 青训营笔记"><i class="fab fa-evernote fa-fw"></i></a></span>
        </div></div><div class="post-nav"><a href="/posts/%E4%B8%80%E6%96%87%E8%BF%9B%E5%85%A5javaweb/" class="prev" rel="prev" title="一文进入JavaWeb后端"><i class="fas fa-angle-left fa-fw"></i>Previous Post</a>
            <a href="/posts/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="next" rel="next" title="消息队列">Next Post<i class="fas fa-angle-right fa-fw"></i></a></div></div>
</div></article></div>
            </main>
            <footer class="footer"><div class="footer-container"><div class="footer-line">由 <a href="https://gohugo.io/" target="_blank" rel="noopener noreffer" title="Hugo 0.86.0">Hugo</a> 强力驱动 | 主题 - <a href="https://github.com/khusika/FeelIt" target="_blank" rel="noopener noreffer" title="FeelIt 1.0.1"><i class="fas fa-hand-holding-heart fa-fw"></i> FeelIt</a>
        </div><div class="footer-line" itemscope itemtype="http://schema.org/CreativeWork"><i class="far fa-copyright fa-fw"></i><span itemprop="copyrightYear">2023</span><span class="author" itemprop="copyrightHolder">&nbsp;<a href="/"></a></span></div>
</div>
</footer>
        </div>

        <div id="fixed-buttons"><a href="#" id="back-to-top" class="fixed-button" title="回到顶部">
                <i class="fas fa-chevron-up fa-fw"></i>
            </a></div><link rel="stylesheet" href="/lib/fontawesome-free/all.min.css"><link rel="stylesheet" href="/lib/animate/animate.min.css"><link rel="stylesheet" href="/lib/katex/katex.min.css"><link rel="stylesheet" href="/lib/katex/copy-tex.min.css"><script src="/lib/autocomplete/autocomplete.min.js"></script><script src="/lib/lunr/lunr.min.js"></script><script src="/lib/lunr/lunr.stemmer.support.min.js"></script><script src="/lib/lunr/lunr.zh.min.js"></script><script src="/lib/lazysizes/lazysizes.min.js"></script><script src="/lib/clipboard/clipboard.min.js"></script><script src="/lib/sharer/sharer.min.js"></script><script src="/lib/katex/katex.min.js"></script><script src="/lib/katex/auto-render.min.js"></script><script src="/lib/katex/copy-tex.min.js"></script><script src="/lib/katex/mhchem.min.js"></script><script>window.config={"code":{"copyTitle":"复制到剪贴板","maxShownLines":200},"comment":{},"math":{"delimiters":[{"display":true,"left":"$$","right":"$$"},{"display":true,"left":"\\[","right":"\\]"},{"display":false,"left":"$","right":"$"},{"display":false,"left":"\\(","right":"\\)"}],"strict":false},"search":{"highlightTag":"em","lunrIndexURL":"/index.json","lunrLanguageCode":"zh","lunrSegmentitURL":"/lib/lunr/lunr.segmentit.js","maxResultLength":100,"noResultsFound":"没有找到结果","snippetLength":50,"type":"lunr"}};</script><script src="/js/theme.min.js"></script></body></html>
